package com.qijia.common.security;

import java.util.ArrayList;  
import java.util.Collection;  
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;  
import java.util.LinkedList;
import java.util.List;  
import java.util.Map;  

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;  
import org.springframework.security.access.ConfigAttribute;  
import org.springframework.security.access.SecurityConfig;  
import org.springframework.security.web.FilterInvocation;  
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;  
import org.springframework.security.web.util.AntUrlPathMatcher;  
import org.springframework.security.web.util.UrlMatcher;  

import com.qijia.common.domain.Resource;
import com.qijia.common.domain.Role;
import com.qijia.common.service.ResouceService;
import com.qijia.common.service.RoleService;
/* 
 *  
 * 最核心的地方，就是提供某个资源对应的权限定义，即getAttributes方法返回的结果。 
 * 注意，我例子中使用的是AntUrlPathMatcher这个path matcher来检查URL是否与资源定义匹配， 
 * 事实上你还要用正则的方式来匹配，或者自己实现一个matcher。 
 *  
 * 此类在初始化时，应该取到所有资源及其对应角色的定义 
 *  
 * 说明：对于方法的spring注入，只能在方法和成员变量里注入， 
 * 如果一个类要进行实例化的时候，不能注入对象和操作对象， 
 * 所以在构造函数里不能进行操作注入的数据。 
 */  


public class InvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {  
    /** 
     * Logger for this class 
     */  
    private static final Logger logger = Logger.getLogger(InvocationSecurityMetadataSourceService.class);  
    
    private RoleService roleService ;  
    
    private ResouceService resouceService;   
      
    private UrlMatcher urlMatcher = new AntUrlPathMatcher();  
    
    private static LinkedList<String> keys = null;
    
    private List<String> publicUrls = null;
    
    private static Map<String, Collection<ConfigAttribute>> resourceMap = null; 
    
    public List<String> getPublicUrls() {
		return publicUrls;
	}

	public void setPublicUrls(List<String> publicUrls) {
		this.publicUrls = publicUrls;
	}

    public  void loadResourceDefine()throws Exception  {  
    	//Map<String, Collection<ConfigAttribute>> tempMap = new HashMap<String, Collection<ConfigAttribute>>();
        resourceMap = new HashMap<String, Collection<ConfigAttribute>>();  
        
        List<Role> roles = roleService.getAllRoles();
        
        //通过数据库中的信息设置，resouce和role  
        for (Role item:roles){  
//            Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();  
//            ConfigAttribute ca = new SecurityConfig(item.getCRoleName());  
//            //超级管理员拥有所有的权限
//            atts.add(adminCas);
//            atts.add(ca);  
            List<Resource> tActionList = resouceService.findByRoleID(item.getCRoleId());  
            
            //把资源放入到spring security的resouceMap中  
            for(Resource tAction:tActionList){  
            	if(null==tAction || StringUtils.isEmpty(tAction.getCResourceUrl())){
            		continue;
            	}
            	
            	if(tAction.getCResourceUrl().indexOf(",")>0){//同时配置多个地址
            		for(String secUrl : tAction.getCResourceUrl().split(",")){
            			addSecureUrlWithAdmin(resourceMap,item.getCRoleName(),secUrl);
            		}
            	}else{
            		addSecureUrlWithAdmin(resourceMap,item.getCRoleName(),tAction.getCResourceUrl());
            	}
//            	if (resourceMap.containsKey(tAction.getCResourceUrl())) {//出现重复的url
//            		atts = new ArrayList<ConfigAttribute>();  
//                    ca = new SecurityConfig(item.getCRoleName());  
//                    atts.add(ca);  
//            		atts.addAll(resourceMap.get(tAction.getCResourceUrl()));
//            		resourceMap.remove(tAction.getCResourceUrl());
//            		resourceMap.put(tAction.getCResourceUrl(), atts);  
//				} else{
//					resourceMap.put(tAction.getCResourceUrl(), atts);  
//				}
            }  
        }  
        
        //赋予超级管理员所有路径的权限,这样既可以防止遗漏的授权,且可以防止未授权的访问
        addSecureUrl(resourceMap,Constants.ROLE_ADMIN,"/**");
        settingList(resourceMap);
        seetingPublicUrls(roles, publicUrls);
        
        
        System.out.println(resourceMap);
        /*//通过硬编码设置，resouce和role 
        resourceMap = new HashMap<String, Collection<ConfigAttribute>>(); 
        Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>(); 
        ConfigAttribute ca = new SecurityConfig("admin");  
        atts.add(ca);  
        resourceMap.put("/jsp/admin.jsp", atts);  
        resourceMap.put("/swf/zara.html", atts);*/   
          
    }  
    
    /**
     * 附予指定的额外的角色覆盖,为了使超级管理可以访问所有的url, 这里附加上超级管理员角色,
     * @param resourceMap
     * @param roleName
     * @param url
     */
    private synchronized void addSecureUrlWithAdmin(Map<String, Collection<ConfigAttribute>> resourceMap,String roleName,String url){
    	ConfigAttribute ca = new SecurityConfig(roleName);
    	Collection<ConfigAttribute> atts = null;
    	
    	if(resourceMap.containsKey(url)){
    		atts = new ArrayList<ConfigAttribute>();  
            ca = new SecurityConfig(roleName);  
            atts.add(ca);  
    		atts.addAll(resourceMap.get(url));
    		resourceMap.remove(url);
    		resourceMap.put(url, atts);  
    	}else{
    		ConfigAttribute adminCas = new SecurityConfig(Constants.ROLE_ADMIN);  
    		atts = new ArrayList<ConfigAttribute>();
    		
    		//超级管理员拥有所有的权限
    		atts.add(adminCas);
    		atts.add(ca);
    		resourceMap.put(url, atts);
    	}
    }
    
    /**
     * 添加权限拦截配置
     * @param resourceMap
     * @param roleName
     * @param url
     */
    private synchronized void addSecureUrl(Map<String, Collection<ConfigAttribute>> resourceMap,String roleName, String url){
    	ConfigAttribute ca = new SecurityConfig(roleName);
    	Collection<ConfigAttribute> atts = null;
    	
    	if(resourceMap.containsKey(url)){
    		atts = new ArrayList<ConfigAttribute>();  
            ca = new SecurityConfig(roleName);  
            atts.add(ca);  
    		atts.addAll(resourceMap.get(url));
    		resourceMap.remove(url);
    		resourceMap.put(url, atts);  
    	}else{
    		atts = new ArrayList<ConfigAttribute>();
    		atts.add(ca);
    		resourceMap.put(url, atts);
    	}
    }
    
    /**
     * 将配置文件中设置的公共url添加至权限mapped中，为了保证公共url能够很好地被识别，
     * 此方法应在settingList方法后执行，因为settingList方法会排序keys，这样会破坏公共url在keys中的位置
     * @param roles
     * @param publicUrls
     */
    private void seetingPublicUrls(List<Role> roles, List<String> publicUrls){
    	//设置配置文件里配置的公共url
        for(Role item : roles){
        	for(String publicUrl : this.publicUrls){
        		addSecureUrl(resourceMap,item.getCRoleName(),publicUrl);
        	}
        }
        
        for(String publicUrl : this.publicUrls){
    		keys.addFirst(publicUrl);
    	}
    }
    
    /**
     * 设置权限配对的key list
     * @param resourceMap
     */
    private void settingList(Map<String, Collection<ConfigAttribute>> resourceMap){
    	keys = new LinkedList<String>(resourceMap.keySet());
    	sortList(keys);
    }
    
    /**
     * 对权限的key list进行排序，将长地址放在最前面，短地址至后，以达到精确的权限拦截
     * @param keys
     */
    private void sortList(List<String> keys){
    	Collections.sort(keys, new Comparator<String>(){
			public int compare(String rm1, String rm2){
				if (null == rm1) {
					return 1;
				} else if (null == rm2) {
					return -1;
				} else if (rm1.length() > rm2.length()) {
					return -1;
				} else if (rm1.length() < rm2.length()) {
					return 1;
				} else {
					return 0;
				}
			}}); 
    }
    
    // According to a URL, Find out permission configuration of this URL.  
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {  
    	
        if (logger.isDebugEnabled()) {  
            logger.debug("getAttributes(Object) - start"); //$NON-NLS-1$  
        }  
        
        // guess object is a URL.  
        String url = ((FilterInvocation) object).getRequestUrl();  
        
        //Iterator<String> ite = resourceMap.keySet().iterator();  
        System.out.println("验证："+url);  
        for (String resURL : keys) {  
            boolean match = urlMatcher.pathMatchesUrl(resURL, url);
            if (match) {  
                Collection<ConfigAttribute> returnCollection = resourceMap.get(resURL);  
                System.out.println(url+"符合Ant Url:"+resURL+" <--> 其对应的角色为:"+returnCollection);  
                if (logger.isDebugEnabled()) {  
                    logger.debug("getAttributes(Object) - end"); //$NON-NLS-1$  
                }  
                return returnCollection;  
            }  
        }  
        if (logger.isDebugEnabled()) {  
            logger.debug("getAttributes(Object) - end"); //$NON-NLS-1$  
        }  
        return null;  
    }  
    public boolean supports(Class<?> clazz) {  
        return true;  
    }  
    public Collection<ConfigAttribute> getAllConfigAttributes() {  
        return null;
    }  
    
    public Collection<ConfigAttribute> getConfigAttributesByKey(String key) {  
        return resourceMap.get(key);
    } 
    
    public RoleService getRoleService() {  
        return roleService;  
    }  
    public void setRoleService(RoleService roleService) {  
        this.roleService = roleService;  
    }  
    public ResouceService getResouceService() {  
        return resouceService;  
    }  
    public void setResouceService(ResouceService resouceService) {  
        this.resouceService = resouceService;  
    }  
    
    public static void main(String[] args){
    	Map<String, Collection<ConfigAttribute>> tempMap = new HashMap<String, Collection<ConfigAttribute>>();
    	Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();  
    	
    	String roleName="ROLE_A";
        ConfigAttribute ca = new SecurityConfig(roleName);  
        atts.add(ca);
        tempMap.put("/a/**", atts);
        tempMap.put("/b/**", atts);
        
        roleName="ROLE_B";
        atts = new ArrayList<ConfigAttribute>();  
        ca = new SecurityConfig(roleName);  
        atts.add(ca);
        atts.addAll(tempMap.get("/a/**"));
        tempMap.put("/a/**", atts);
        
        System.out.println(tempMap);
        
        Map<String, Collection<String>> tempMap1 = new HashMap<String, Collection<String>>();
    	Collection<String> atts1 = new ArrayList<String>();  
    	
    	roleName="ROLE_A";
        atts1.add(roleName);
        tempMap1.put("/a/**", atts1);
        tempMap1.put("/b/**", atts1);
        
        roleName="ROLE_B";
        atts1 = new ArrayList<String>();  
        atts1.add(roleName);
        System.out.println(tempMap1.get("/a/**"));
        System.out.println(tempMap1.get("/b/**"));
        atts1.addAll(tempMap1.get("/a/**"));
        tempMap1.put("/a/**", atts1);
        
        //tempMap1.get("/a/**").add(roleName);
        
        System.out.println(tempMap1);
        
    }
}   